#include "numeric.h"

// Default constructor declares an empty type
numeric::numeric() : valuetype(empty) { }
// Copy constructor declares a type identical to the numeric n.
numeric::numeric(const numeric &n) : valuetype(n.valuetype) 
{
	switch(valuetype)
	{
	case sizetype:
		sizevalue = (size_t)n;
		break;
	case longtype:
		longvalue = (long)n;
		break;
	case doubletype:
		doublevalue = (double)n;
		break;
	case complextype:
		cxvalue.real = complex(n).real();
		cxvalue.imag = complex(n).imag();
		break;
	default:
		break;
	}
}	
// Copy constructor declares an size_t type
numeric::numeric(const size_t &n) : valuetype(sizetype) { sizevalue = n; }
// Copy constructor declares an long type
numeric::numeric(const long &n) : valuetype(longtype) { longvalue = n; }
// Copy constructor declares an double type
numeric::numeric(const double &n) : valuetype(doubletype) { doublevalue = n; }
// Copy constructor declares an complex type
numeric::numeric(const complex &n) : valuetype(complextype) { cxvalue.real = n.real(); cxvalue.imag = n.imag(); }

// Assignment operator declares a type identical to the numeric n.
numeric &numeric::operator=(const numeric &n)
{
	valuetype = n.valuetype;

	switch(valuetype)
	{
	case sizetype:
		sizevalue = (size_t)n;
		break;
	case longtype:
		longvalue = (long)n;
		break;
	case doubletype:
		doublevalue = (double)n;
		break;
	case complextype:
		cxvalue.real = complex(n).real();
		cxvalue.imag = complex(n).imag();
		break;
	default:
		break;
	}
	return *this;
}
		
// Type getter function.
numeric::type numeric::getType() const { return valuetype; }	
// Tests for empty type, same as getType() == numeric::empty.
bool numeric::isEmpty() const { return valuetype == empty; }		
// Tests for size_t type, same as getType() == numeric::sizetype.
bool numeric::isSizeType() const { return valuetype == sizetype; }		
// Tests for long type, same as getType() == numeric::longtype.
bool numeric::isLongType() const { return valuetype == longtype; }	
// Tests for double type, same as getType() == numeric::doubletype.
bool numeric::isDoubleType() const { return valuetype == doubletype; }	
// Tests for complex type, same as getType() == numeric::complextype.
bool numeric::isComplexType() const { return valuetype == complextype; }	
	
// Explicit conversion to size_t function.
size_t numeric::toSize() const
{
	switch(valuetype)
	{
	case sizetype:
		return sizevalue;
		break;
	case longtype:
		return (longvalue > 0) ? size_t(longvalue) : 0;
		break;
	case doubletype:
		return (doublevalue > 0.) ? size_t(doublevalue) : 0;
		break;
	case complextype:
		return (cxvalue.real > 0.) ? size_t(cxvalue.real) : 0;
		break;
	default:
		return 0;
	}
	return 0;
}
// Explicit conversion long function.
long numeric::toLong() const
{
	switch(valuetype)
	{
	case sizetype:
		return long(sizevalue);
		break;
	case longtype:
		return longvalue;
		break;
	case doubletype:
		return long(doublevalue);
		break;
	case complextype:
		return long(cxvalue.real);
		break;
	default:
		return 0;
	}
	return 0;
}	
// Explicit conversion double function.
double numeric::toDouble() const
{
	switch(valuetype)
	{
	case sizetype:
		return double(sizevalue);
		break;
	case longtype:
		return double(longvalue);
		break;
	case doubletype:
		return doublevalue;
		break;
	case complextype:
		return cxvalue.real;
		break;
	default:
		return 0.;
	}
	return 0.;
}
// Explicit conversion complex function.
complex numeric::toComplex() const
{
	switch(valuetype)
	{
	case sizetype:
		return complex(sizevalue,0.);
		break;
	case longtype:
		return complex(longvalue,0.);
		break;
	case doubletype:
		return complex(doublevalue,0.);
		break;
	case complextype:
		return complex(cxvalue.real,cxvalue.imag);
		break;
	default:
		return complex(0.,0.);
	}
	return complex(0.,0.);
}
	
// Implicit conversion to size_t function.
numeric::operator size_t() const { return toSize(); }
// Implicit conversion to long function.
numeric::operator long() const { return toLong(); }	
// Implicit conversion to double function.
numeric::operator double() const { return toDouble(); }
// Implicit conversion to complex function.
numeric::operator complex() const { return toComplex(); }
	
// numeric relation operator, mimics that of ordinary numerical types.
numeric &numeric::operator+=(const numeric &n)
{
	if(n.isEmpty())
		return *this;

	switch(valuetype)
	{
	case sizetype:
		if(n.isSizeType())
			sizevalue += size_t(n);
		else if(n.isLongType())
			*this = long(sizevalue) + long(n);
		else if(n.isDoubleType())
			*this = double(sizevalue) + double(n);
		else if(n.isComplexType())
			*this = complex(sizevalue,0.) + complex(n);
		break;
	case longtype:
		if(n.isSizeType() || n.isLongType())
			longvalue += long(n);
		else if(n.isDoubleType())
			*this = double(longvalue) + double(n);
		else if(n.isComplexType())
			*this = complex(longvalue,0.) + complex(n);
		break;
	case doubletype:
		if(n.isSizeType() || n.isLongType() || n.isDoubleType())
			doublevalue += double(n);
		else if(n.isComplexType())
			*this = complex(doublevalue,0.) + complex(n);
		break;
	case complextype:
		cxvalue.real += complex(n).real();
		cxvalue.imag += complex(n).imag();
		break;
	default:
		return *this;
	}
	return *this;
}
// numeric relation operator, mimics that of ordinary numerical types.
numeric &numeric::operator-=(const numeric &n)
{
	if(n.isEmpty())
		return *this;

	switch(valuetype)
	{
	case sizetype:
		if(n.isSizeType() && long(sizevalue) - long(n) >= 0)
			sizevalue -= size_t(n);
		else if(n.isSizeType() || n.isLongType())
			*this = long(sizevalue) - long(n);
		else if(n.isDoubleType())
			*this = double(sizevalue) - double(n);
		else if(n.isComplexType())
			*this = complex(sizevalue,0.) - complex(n);
		break;
	case longtype:
		if(n.isSizeType() || n.isLongType())
			longvalue -= long(n);
		else if(n.isDoubleType())
			*this = double(longvalue) - double(n);
		else if(n.isComplexType())
			*this = complex(longvalue,0.) - complex(n);
		break;
	case doubletype:
		if(n.isSizeType() || n.isLongType() || n.isDoubleType())
			doublevalue -= double(n);
		else if(n.isComplexType())
			*this = complex(doublevalue,0.) - complex(n);
		break;
	case complextype:
		cxvalue.real -= complex(n).real();
		cxvalue.imag -= complex(n).imag();
		break;
	default:
		return *this;
	}
	return *this;
}
// numeric relation operator, mimics that of ordinary numerical types.
numeric &numeric::operator*=(const numeric &n)
{
	if(n.isEmpty())
		return *this;

	switch(valuetype)
	{
	case sizetype:
		if(n.isSizeType())
			sizevalue *= size_t(n);
		else if(n.isLongType())
			*this = long(sizevalue) * long(n);
		else if(n.isDoubleType())
			*this = double(sizevalue) * double(n);
		else if(n.isComplexType())
			*this = complex(sizevalue,0.) * complex(n);
		break;
	case longtype:
		if(n.isSizeType() || n.isLongType())
			longvalue *= long(n);
		else if(n.isDoubleType())
			*this = double(longvalue) * double(n);
		else if(n.isComplexType())
			*this = complex(longvalue,0.) * complex(n);
		break;
	case doubletype:
		if(n.isSizeType() || n.isLongType() || n.isDoubleType())
			doublevalue *= double(n);
		else if(n.isComplexType())
			*this = complex(doublevalue,0.) * complex(n);
		break;
	case complextype:
		*this = complex(*this) * complex(n);
		break;
	default:
		return *this;
	}
	return *this;
}
// numeric relation operator, mimics that of ordinary numerical types.
numeric &numeric::operator/=(const numeric &n)
{
	if(n.isEmpty())
		return *this;

	switch(valuetype)
	{
	case sizetype:
		if(n.isSizeType())
			sizevalue /= size_t(n);
		else if(n.isLongType())
			*this = long(sizevalue) / long(n);
		else if(n.isDoubleType())
			*this = double(sizevalue) / double(n);
		else if(n.isComplexType())
			*this = complex(sizevalue,0.) / complex(n);
		break;
	case longtype:
		if(n.isSizeType() || n.isLongType())
			longvalue /= long(n);
		else if(n.isDoubleType())
			*this = double(longvalue) / double(n);
		else if(n.isComplexType())
			*this = complex(longvalue,0.) / complex(n);
		break;
	case doubletype:
		if(n.isSizeType() || n.isLongType() || n.isDoubleType())
			doublevalue /= double(n);
		else if(n.isComplexType())
			*this = complex(doublevalue,0.) / complex(n);
		break;
	case complextype:
		*this = complex(*this) / complex(n);
		break;
	default:
		return *this;
	}
	return *this;
}
// numeric relation operator, mimics that of ordinary numerical types.
numeric numeric::operator+(const numeric &n) const { numeric r(*this); return (r += n); }
// numeric relation operator, mimics that of ordinary numerical types.
numeric numeric::operator-(const numeric &n) const { numeric r(*this); return (r -= n); }
// numeric relation operator, mimics that of ordinary numerical types.
numeric numeric::operator*(const numeric &n) const { numeric r(*this); return (r *= n); }
// numeric relation operator, mimics that of ordinary numerical types.
numeric numeric::operator/(const numeric &n) const { numeric r(*this); return (r /= n); }

// Named constructor that returns a range with begin and end as provided and a step calculated from the size provided.
numeric::range numeric::interval(const numeric &b, const numeric &e, const size_t s) { range r = { b, e, (e-b)/numeric(s-1) }; return r; }
// Named constructor that returns a range with begin, end and step as provided.
numeric::range numeric::span(const numeric &b, const numeric &e, const numeric s) { range r = { b, e, s }; return r; }